package com.heima.schedule.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.common.constants.ScheduleConstants;
import com.heima.common.redis.RedisCacheService;
import com.heima.model.schedule.dtos.Task;
import com.heima.model.schedule.pojos.Taskinfo;
import com.heima.model.schedule.pojos.TaskinfoLogs;
import com.heima.schedule.mapper.TaskinfoLogsMapper;
import com.heima.schedule.mapper.TaskinfoMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.management.openmbean.TabularType;
import java.security.PublicKey;
import java.time.LocalTime;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * 任务接口
 */
@Service
public class TaskService {

    @Autowired
    private TaskinfoMapper taskinfoMapper;

    @Autowired
    private TaskinfoLogsMapper taskinfoLogsMapper;

    @Autowired
    private RedisCacheService redisCacheService;

    /**
     * 添加任务方法
     * @param task
     */
    public void saveTask(Task task){
        //1.保存数据到mysql中
        boolean flag = saveTaskToDb(task);



        if(flag){
            //2.同步数据到redis中
            taskinfoToRedis(task);
        }
    }

    /**
     * 保存任务到数据库表中
     * @param task
     * @return
     */
    private boolean saveTaskToDb(Task task) {
        Taskinfo taskinfo=new Taskinfo();
        BeanUtils.copyProperties(task,taskinfo);
        taskinfo.setExecuteTime(new Date(task.getExecuteTime()));
        taskinfoMapper.insert(taskinfo);

        //把生成的taskinfo的id赋值给task对象
        task.setTaskId(taskinfo.getTaskId());


        TaskinfoLogs taskinfologs=new TaskinfoLogs();
        BeanUtils.copyProperties(taskinfo,taskinfologs);
        taskinfologs.setVersion(1);//默认从1开始
        taskinfologs.setStatus(ScheduleConstants.SCHEDULED);//初始化状态，1表示执行，2表示取消
        int insert = taskinfoLogsMapper.insert(taskinfologs);
        return insert==1;
    }

    /**
     * 抽取的方法，同步数据到redis中
     *  如果任务中的执行时间小于等于当前系统是哪，则存储到redis中list集合中
     *  如果任务中的执行时间小于等于当前系统时间+5分钟，则存入到redis中的zset中
     */
    private void taskinfoToRedis(Task task) {
        String key = task.getTaskType() + "_" + task.getPriority();
        //如果任务中的执行时间小于等于当前系统是哪，则存储到redis中list集合中
        if(task.getExecuteTime()<=System.currentTimeMillis()){
            redisCacheService.lLeftPush(ScheduleConstants.TOPIC+key, JSON.toJSONString(task));
        }
        //如果任务中的执行时间小于等于当前系统时间+5分钟，则存入到redis中的zset中
            //获取未来五分钟的时间毫秒
        Calendar calendar=Calendar.getInstance();
        calendar.add(Calendar.MINUTE,5);

        if(task.getExecuteTime()<=calendar.getTimeInMillis()&& task.getExecuteTime()>System.currentTimeMillis()){
            redisCacheService.zAdd(ScheduleConstants.FUTURE+key,JSON.toJSONString(task),task.getExecuteTime());
        }
    }

    /**
     * 拉取或者消费任务
     * 主要从redis中取出数据，然后删除taskinfo及修改taskinfologs
     */
    public Task pullTask(int type,int priority){
        String key = type + "_" + priority;
        //1.从redis中取出数据,并删除
        String str = redisCacheService.lRightPop(ScheduleConstants.TOPIC + key);

        //1.5 转类型
        Task task = JSON.parseObject(str, Task.class);
        //2.删除taskinfo及修改taskinfologs
        taskinfoMapper.deleteById(task.getTaskId());

        TaskinfoLogs taskinfoLogs = taskinfoLogsMapper.selectById(task.getTaskId());
        if(taskinfoLogs!=null){
            taskinfoLogs.setStatus(ScheduleConstants.EXECUTED);
            taskinfoLogsMapper.updateById(taskinfoLogs);
        }
        return task;
    }

    /**
     * 定时刷新同步数据从zset中到list
     * 注意：该方法不会被任何接口调用，他会自动按照定时任务执行
     */
    @Scheduled(cron = "0 * * * * ?")
    public void refreshZsetToList(){
        //表示加锁
        String token = redisCacheService.tryLock("FUTURE_TASK_SYNC", 30 * 1000);
        if(StringUtils.isNotBlank(token)){
            System.out.println("定时任务开始了》》》》"+ LocalTime.now());
            //1.获取所有未来数据的key
            Set<String> futeKeys = redisCacheService.scan(ScheduleConstants.FUTURE + "*");
            for (String futeKey : futeKeys) {
                //按照分值并查询小于当前时间的数据,tasks是所有的value
                Set<String> tasks = redisCacheService.zRangeByScore(futeKey, 0, System.currentTimeMillis());
                /**
                 *  2.利用管道的方式同步到list中
                 * refreshWithPipeline(String future_key,String topic_key,Collection<String> values)
                 * 参数1表示未来zset中的key
                 * 参数2表示list中的key
                 * 参数3表示同步数据的集合
                 */
                String topicKey=ScheduleConstants.TOPIC+futeKey.split(ScheduleConstants.FUTURE)[1];

                redisCacheService.refreshWithPipeline(futeKey,topicKey,tasks);
            }
        }
    }

    /**
     * 定时同步数据从数据库中到redis中
     */
    @Scheduled(cron = "0 0/5 * * * ?")
    public void syncDbToRedis(){
            //1.清楚缓存
        Set<String> topicKeys = redisCacheService.scan(ScheduleConstants.TOPIC + "*");
        Set<String> futureKeys = redisCacheService.scan(ScheduleConstants.FUTURE + "*");
        redisCacheService.delete(topicKeys);
        redisCacheService.delete(futureKeys);

        //2.查询mysql数据库
        Calendar calendar=Calendar.getInstance();
        calendar.add(Calendar.MINUTE,5);

        List<Taskinfo> taskinfos = taskinfoMapper.selectList(
                Wrappers.<Taskinfo>lambdaQuery().lt(Taskinfo::getExecuteTime, calendar.getTimeInMillis())
        );

        //3.新增数据到redis中
        for (Taskinfo taskinfo : taskinfos) {
            Task task=new Task();
            BeanUtils.copyProperties(taskinfo,task);
           task.setExecuteTime(taskinfo.getExecuteTime().getTime());
            this.taskinfoToRedis(task);
        }

   }































}
